
// Import the necessary modules
import * as fs from 'fs';
import * as path from 'path';
import { ValueError } from '../exceptions'



class IniConfig {
    private _conf:Record<string,Record<string,any>> = {}
    constructor(private _filePath: string = "") { }

    get value(){
        return this._conf;
    }

    /**
     * 从一个对象加载配置文件信息
     * @param obj 要载入为 ini 配置信息的对象
     */
    public load(obj: Record<string, Record<string, any>>) {
        this._conf = obj
    }

    /**
     * 
     * @param filePath 被加载的 ini 文件的绝对路径，如果该参数指定，则实例化时指定的路径将被覆盖
     * @returns {this} 返回对实例的引用，以方便链式调用
     */
    public readSync<T>(filePath?: string): this {
        if (filePath) {
            this._filePath = filePath
        }
        const fileContents = fs.readFileSync(this._filePath, 'utf-8')
        const lines = fileContents.split('\n');

        let section = '';
        for (const line of lines) {
            const trimmedLine = line.trim();
            // 注释
            if (trimmedLine.startsWith('#') || trimmedLine === '') {
                continue;
            }
            // section state
            if (/^\[.*\]$/.test(trimmedLine)) {
                section = trimmedLine.replace(/^\[/, '').replace(/\]$/, '')
            } 
            else if(this._conf[section]) {
                const [key, value] = trimmedLine.split('=');
                this._conf[section][key] = value;
            }else{
                this._conf[section] = {};
                const [key, value] = trimmedLine.split('=');
                this._conf[section][key] = value;
            }
        }
        return this;
    }

    /**
     * 将一个对象写入磁盘为 ini 配置文件
     * @param obj 
     * @returns 
     */
    public static format<U=Record<string,Record<string,any>>>(obj:U) {
        let iniStr = '';
        for (let section in obj) {
            iniStr += `[${section}]\n`;
            for (let key in obj[section]) {
                iniStr += `${key}=${obj[section][key]}\n`;
            }
        }
        return iniStr;
    }

    private _farmat(): string{
        let iniStr = '';
        for (let section in this._conf) {
            iniStr += `[${section}]\n`;
            for (let key in this._conf[section]) {
                iniStr += `${key}=${this._conf[section][key]}\n`;
            }
        }
        return iniStr;
    }

    /**
     * 将 ini 配置对象转换为字符串并写入磁盘
     * 
     * @param filePath 写入磁盘的路径
     * - 如果指定了新的 filePath 将会覆盖旧的 filePath
     */
    public writeSync(filePath?: string):void {
        if (filePath) {
            this._filePath = filePath
        }
        fs.writeFileSync(this._filePath,this._farmat())
    }

    /**
     * 添加一个新的 Section 
     * @param section 
     * @param records 
     * @returns {this} 返回对实例的引用，以方便链式调用
     */
    public addSection<V extends Record<string, any>>(section: string, records: V): this {
        if (section === "") {
            section = 'DEFAULT'
        }
        this._conf[section] = { ...records }
        return this
    }

    /**
     * 添加一个新的记录，默认添加到 DEFAULT Ssection
     * @param section 要添加的 section 的名称
     * @param key 记录的 key 
     * @param value 记录的 value
     * @returns {this} 返回对实例的引用，以方便链式调用
     */
    public addRecord<V>(section: string = "DEFAULT", key: string, value: V): this {
        if(section===''){
            section = 'DEFAULT'
        }
        if(!this._conf[section]){
            this._conf[section] = {}
        }
        if(key===""){
            throw ValueError("The value of param \"key\" could not be a empty string.")
        }
        this._conf[section][key] = value
        
        return this
    }

    /**
     * 
     * @param section 要获取的 section 的名称
     * @returns 若 section 不存在则返回一个空对象
     */
    public getSection(section: string="DEFAULT"): Record<string, any> {
        if(section!==""){
            return this._conf[section]
        }
        return {}
    }

    public get(section: string = "DEFAULT", key?: string):any {
        if (this._conf[section]) {
            const _section = this._conf[section]
            if (key) {
                return _section[key]
            }
        }

    }

    private __str__() {
        return JSON.stringify(this._conf, null, 2)
    }

    public print():this {
        console.log(this.__str__());
        return this
    }
}

export {
  IniConfig
}